// statemanager.cpp : Defines a class for managing object oriented game states
// to control game flow
//$Id: StateManager.cpp 5 2009-07-27 17:33:21Z brwarner2 $

#include "StdAfx.h"
#include "StateManager.h"
#include "GameState.h"

typedef std::map< std::string, IGameState*>::iterator MapIterator;
typedef std::list< IGameState* >::iterator ListIterator;
typedef std::list< IGameState* >::reverse_iterator ListRIterator;

CStateManager::CStateManager(void) : m_bPop(false), m_nextStateName(), m_registeredStates(),
	m_stateStack(), m_bFlush(false), m_pNextState(0), m_pPushState(0),
	m_pushStateName()
{
}

CStateManager::~CStateManager(void)
{
	//Release the state manager
	Release();
}
void CStateManager::Render(void)
{
	//Render all active states
	std::list<IGameState*>::iterator i = m_stateStack.begin();
	for(; i != m_stateStack.end(); std::advance(i, 1))
		(*i)->Render( );
}

void CStateManager::SendNotify(const std::string &state, const std::string msg, int nArg, const boost::any &pArg)
{
	try
	{
		//Attempt to send that state a message
		m_registeredStates[state]->Notify( msg, nArg, pArg );
	}
	catch( std::out_of_range& ) {}
}

bool CStateManager::RegisterState(const std::string &name, IGameState *pState)
{
	//Check to see if the state already exists
	if( m_registeredStates.find(name) != m_registeredStates.end() )
		return false;

	//Set the state's parent to ourself
	pState->SetParent(this);

	//Insert it into the map
	m_registeredStates.insert( make_pair( name, pState ) );
	return true;
}

void CStateManager::Release( void )
{
	//Exit current state
	if( ! m_stateStack.empty() )
		(*m_stateStack.rbegin())->OnExit( "" );

	//Empty the stack
	m_stateStack.clear();

	//Release each state
	MapIterator i = m_registeredStates.begin();
	for(; i != m_registeredStates.end(); std::advance(i,1))
	{
		delete i->second;
	}
	m_registeredStates.clear();

	//Clear things
	m_pNextState = m_pPushState = 0;
	m_bPop = m_bFlush = false;
	m_nextStateName = m_pushStateName = "";
}

void CStateManager::ChangeState(const std::string &name, bool bFlush)
{
	//Find the state in the map
	MapIterator iter = m_registeredStates.find(name);
	if(iter == m_registeredStates.end()) return;

	//Set the next state to this
	m_pNextState = iter->second;
	m_nextStateName = iter->first;
	m_bFlush = bFlush;
}

void CStateManager::PushState(const std::string &state)
{
	//Check if there is a stack
	if(m_stateStack.empty()) return;

	//Find the state in the map
	MapIterator iter = m_registeredStates.find(state);
	if(iter == m_registeredStates.end()) return;

	//Set it as the next push
	m_pPushState = iter->second;
	m_pushStateName = state;
}

void CStateManager::PopState()
{
	//Set popper
	m_bPop = true;
}

void CStateManager::Update( float fElapsed, float fTotal )
{
	//First, take care of state changing/popping/pushing
	if(m_pNextState != 0)
	{
		//If there is no flushing
		if(!m_bFlush)
		{
			//Exit the previous state
			ListRIterator last = m_stateStack.rbegin();
			(*last)->OnExit( m_nextStateName );

			//Store it
			IGameState* oldState = *last;

			//Change it
			*last = m_pNextState;
			m_pNextState->OnEnter( "LastState" );
		}
		else
		{
			//Exit all states
			ListRIterator last = m_stateStack.rbegin();
			for(; last != m_stateStack.rend(); last++)
				(*last)->OnExit( m_nextStateName );
			m_stateStack.clear();

			//Insert the new state
			m_stateStack.push_back( m_pNextState );
			m_pNextState->OnEnter( "LastState" );
		}
	}
	m_pNextState = m_pPushState = 0;
	m_nextStateName = m_pushStateName = "";

	//Update all the states
	std::list<IGameState*>::iterator i = m_stateStack.begin();
	for(; i != m_stateStack.end(); std::advance(i, 1))
		(*i)->Update( fElapsed, fTotal );
}
